package ru.aptu.billing_server;


import ru.aptu.billing_message.BasicMessage;
import ru.aptu.billing_message.RequestMessage;
import ru.aptu.billing_message.ResponseMessage;
import ru.aptu.socket_factory.BillingServerSocketFactory;
import ru.aptu.socket_factory.ConnectionChecker;

import javax.net.ssl.SSLHandshakeException;
import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * Created by IntelliJ IDEA.
 * User: andrey
 * Date: 12/22/11
 * Time: 8:49 PM
 */
public abstract class SSLServer {

    protected String host;

    protected int port;

    private ExecutorService pool;

    protected SSLServer(String host, int port) {
        this.port = port;
        this.host = host;
        pool = Executors.newCachedThreadPool();
    }

    public class ClientHandler implements Runnable {

        protected Socket socket;

        public ClientHandler(Socket socket) {
            this.socket = socket;
        }

        public void run() {

            try {
                ConnectionChecker.checkServerConnection(socket);

                ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
                ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());

                //noinspection InfiniteLoopStatement
                while (true) {
                    BasicMessage message = (BasicMessage) objectInputStream.readObject();
                    System.err.println("Message received");

                    if (!message.getDirection().equals(BasicMessage.MessageDirection.REQUEST)) {
                        objectOutputStream.writeObject(badMessage(message));
                    } else if (message.getType().equals(BasicMessage.MessageType.CHECK_PROTOCOL)) {
                        objectOutputStream.writeObject(checkProtocol((RequestMessage) message));
                    } else {
                        objectOutputStream.writeObject(handleRequest((RequestMessage) message));
                    }
                }
            } catch (SSLHandshakeException ex) {
                System.err.println("Authorization failed");
            } catch (EOFException ex) {
                System.err.println("Client disconnected");
            } catch (Exception exception) {
                System.err.println("Internal error:");
                exception.printStackTrace();
            } finally {
                try {
                    socket.close();
                } catch (IOException e) {
                    System.err.println("Failed to close connection.");
                }
            }

        }

    }


    public void startServer() {
        try {
            ServerSocket serverSocket = BillingServerSocketFactory.createServerSocket(port, InetAddress.getByName(host));

            //noinspection InfiniteLoopStatement
            while (true) {
                ClientHandler clientHandler = new ClientHandler(serverSocket.accept());
                System.err.println("Client connected");

                pool.submit(clientHandler);
            }
        } catch (SSLHandshakeException exception) {
            exception.printStackTrace();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    protected ResponseMessage badMessage(BasicMessage message) {
        return new ResponseMessage(message, BasicMessage.Status.BAD_MESSAGE);
    }

    protected ResponseMessage checkProtocol(RequestMessage message) {
        return new ResponseMessage(message,
                (message.getProtocolVersion() == BasicMessage.currentProtocolVersion) ? BasicMessage.Status.OK : BasicMessage.Status.UNSUPPORTED_PROTOCOL);
    }

    protected abstract ResponseMessage handleRequest(RequestMessage message);
}
